home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue67 / express / Parser10.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2001-01-31  |  25.6 KB  |  1,002 lines

  1. {***************************************************************}
  2. {                                                               }
  3. { TParser 10.2 for Borland Delphi                               }
  4. {                                                               }
  5. { A component for parsing and evaluating mathematical           }
  6. { expressions specified at runtime                              }
  7. {                                                               }
  8. { Developed by                                                  }
  9. {   Renate Schaaf (schaaf@math.usu.edu), 1993                   }
  10. {   Alin Flaider (aflaidar@datalog.ro), 1996                    }
  11. {   Stefan Hoffmeister (Stefan.Hoffmeister@Uni-Passau.de), 1997 }
  12. {   Hallvard Vassbotn (hallvard.vassbotn@c2i.net),              }
  13. {     Added dynamic generation of code (DYNAMIC_CODE), 1999     }
  14. {     Simplified a little for TDM article, 2000                 }
  15. {                                                               }
  16. { See  PARSER10.TXT  for documentation                          }
  17. {                                                               }
  18. {***************************************************************}
  19. unit Parser10;
  20.  
  21. {$IFDEF Win32}
  22.   {$H+} { long strings }
  23. {$ENDIF}
  24.  
  25. {$DEFINE DYNAMIC_CODE} { Compile assembly code dynamically,
  26.                          gives a major performance boost on PII and later }
  27. {$I+} { I/O checking ON }
  28.  
  29. interface
  30.  
  31. uses SysUtils, Classes;
  32.  
  33. type
  34.   PParserFloat = ^ParserFloat;
  35.   ParserFloat  = Double;
  36.   TToken = (variab, constant, minus, sum, diff, prod, divis, modulo, IntDiv,
  37.     integerpower, realpower, square, third, fourth, FuncOneVar, FuncTwoVar);
  38.  
  39.   POperation = ^TOperation;
  40.   TMathProcedure = procedure(AnOperation: POperation);
  41.   TOperation = record
  42.     Arg1: PParserFloat;
  43.     Arg2: PParserFloat;
  44.     Dest: PParserFloat;
  45.     NextOperation: POperation;
  46.     MathProc: TMathProcedure;
  47.     Token: TToken;
  48.   end;
  49.  
  50.   EMathParserError      = class(Exception);
  51.     ESyntaxError          = class(EMathParserError);
  52.     EExpressionHasBlanks  = class(EMathParserError);
  53.     EExpressionTooComplex = class(EMathParserError);
  54.     ETooManyNestings      = class(EMathParserError);
  55.     EMissMatchingBracket  = class(EMathParserError);
  56.     EBadName              = class(EMathParserError);
  57.     EParserInternalError  = class(EMathParserError);
  58.  
  59.   TParserExceptionEvent = procedure (Sender: TObject; E: Exception) of object;
  60.  
  61.   TCustomParser = class(TComponent)
  62.   private
  63. {$IFDEF DYNAMIC_CODE}
  64.     DynamicCode         : Pointer;
  65. {$ENDIF DYNAMIC_CODE}
  66.     FExpression         : string;
  67.     FPascalNumberformat : boolean;
  68.     FParserError        : boolean;
  69.     FVariables          : TStringList;
  70.     FStartOperationList : POperation;
  71.     FOnParserError      : TParserExceptionEvent;
  72.     FFunctionOne : TStringList;
  73.     FFunctionTwo : TStringList;
  74.     function GetValue: Extended;
  75.     procedure SetExpression(const AnExpression: string);
  76.     procedure SetVar(const VarName: string; const Value: Extended);
  77.     procedure SetValue(const Value: Extended);
  78.     function ParseExpression: boolean;
  79.     procedure FreeExpression;
  80.     function GetVariable(const VarName: string): Extended;
  81.     procedure DisposeVariableFloatAt(Index: integer);
  82.     function ConnectMandatoryOperations: integer;
  83.     procedure GenerateDynamicCode(OperationCount: integer);
  84.   protected
  85.     procedure DefineBasicFunctions;
  86.     property FunctionOne : TStringList read FFunctionOne;
  87.     property FunctionTwo : TStringList read FFunctionTwo;
  88.     property LinkedOperationList: POperation read FStartOperationList;
  89.   public
  90.     constructor Create(AOwner: TComponent); override;
  91.     destructor Destroy; override;
  92.     { Function support }
  93.     procedure AddFunctionOneParam(const AFunctionName: string; const Func: TMathProcedure);
  94.     procedure AddFunctionTwoParam(const AFunctionName: string; const Func: TMathProcedure);
  95.     procedure ClearFunctions;
  96.     procedure ClearFunction(const AFunctionName: string);
  97.     { Variable support }
  98.     procedure ClearVariables;
  99.     procedure ClearVariable(const AVarName: string);
  100.     function  VariableExists(const AVarName: string): boolean;
  101.     function SetVariable(VarName: string; const Value: Extended): PParserFloat;
  102.     property Variable[const VarName: string]: Extended read GetVariable write SetVar;
  103.     { Error handling }
  104.     property ParserError: boolean read FParserError;
  105.   published
  106.     { To evaluate an expression simply read the Value property. }
  107.     property Value: Extended read GetValue write SetValue stored False;
  108.     property Expression: string read FExpression write SetExpression;
  109.     property PascalNumberformat: boolean read FPascalNumberformat write FPascalNumberformat default True;
  110.     property OnParserError: TParserExceptionEvent read FOnParserError write FOnParserError;
  111.   end;
  112.  
  113.   TParser = class(TCustomParser)
  114.   private
  115.     { some pre-allocated space for variables }
  116.     FA: ParserFloat;
  117.     FB: ParserFloat;
  118.     FC: ParserFloat;
  119.     FD: ParserFloat;
  120.     FE: ParserFloat;
  121.     FX: ParserFloat;
  122.     FY: ParserFloat;
  123.     FT: ParserFloat;
  124.   public
  125.     constructor Create(AOwner: TComponent); override;
  126.   published
  127.     { predefined variable properties }
  128.     property A: ParserFloat read FA write FA;
  129.     property B: ParserFloat read FB write FB;
  130.     property C: ParserFloat read FC write FC;
  131.     property D: ParserFloat read FD write FD;
  132.     property E: ParserFloat read FE write FE;
  133.     property T: ParserFloat read FT write FT;
  134.     property X: ParserFloat read FX write FX;
  135.     property Y: ParserFloat read FY write FY;
  136.  end;
  137.  
  138. procedure Register;
  139.  
  140. implementation
  141.  
  142. {$DEFINE UseMath}
  143. { Note: if you do not have the MATH unit simply remove the conditional define
  144.         the component will continue to work, just a bit slower }
  145.  
  146. uses
  147. {$IFDEF UseMath}
  148.   Math,
  149. {$ENDIF}
  150.   P10Build;
  151.  
  152. (*
  153. {$IFDEF VER80}
  154.   {$R *.D16}
  155. {$ELSE}
  156.   {$IFDEF VER90}
  157.     {$R *.D32}
  158.   {$ENDIF}
  159. {$ENDIF}
  160. *)
  161.  
  162. procedure Register;
  163. begin
  164.   RegisterComponents('Samples', [TParser]);
  165. end;
  166.  
  167. function PointerToSelfInstance(Self: TObject; Ptr: Pointer): boolean;
  168. { Return True of the Pointer points inside the object instance }
  169. begin
  170.   Result := Assigned(Self) and
  171.             (Longint(Ptr) >= Longint(Self)) and
  172.             (Longint(Ptr) <= Longint(Self) + Self.InstanceSize);
  173. end;
  174.  
  175. {****************************************************************}
  176. {                                                                }
  177. {   Following are "built-in" calculation procedures              }
  178. {                                                                }
  179. {****************************************************************}
  180. {
  181. Naming convention for functions:
  182.  
  183.   Name of built-in function, prepended with an underscore.
  184.   Example:
  185.  
  186.     ln --> _ln
  187.  
  188. Passed arguments / results:
  189.  
  190.   If the function takes any arguments - i.e. if it has been added to
  191.   either the FunctionOne or the FunctionTwo list:
  192.  
  193.   - First  argument --> arg1^
  194.   - Second argument --> arg2^
  195.  
  196.   The result of the operation must ALWAYS be put into
  197.  
  198.      dest^
  199.  
  200.  
  201.  Note: These are POINTERS to floats.
  202. }
  203.  
  204. {****************************************************************}
  205. {                                                                }
  206. {   These are mandatory procedures - never remove them           }
  207. {                                                                }
  208. {****************************************************************}
  209.  
  210. procedure _nothing(AnOp: POperation); far;
  211. { do nothing - this only happens if the "term" is just a number
  212.   or a variable; otherwise this procedure will never be called }
  213. begin
  214. end;
  215.  
  216. procedure _Add(AnOp: POperation); far;
  217. begin
  218.   with AnOp^ do
  219.     dest^ := arg1^ + arg2^;
  220. end;
  221.  
  222. procedure _Subtract(AnOp: POperation); far;
  223. begin
  224.   with AnOp^ do
  225.     dest^ := arg1^ - arg2^;
  226. end;
  227.  
  228. procedure _Multiply(AnOp: POperation); far;
  229. begin
  230.   with AnOp^ do
  231.     dest^ := arg1^ * arg2^;
  232. end;
  233.  
  234. procedure _RealDivide(AnOp: POperation); far;
  235. begin
  236.   with AnOp^ do
  237.     dest^ := arg1^ / arg2^;
  238. end;
  239.  
  240. procedure _Modulo(AnOp: POperation); far;
  241. begin
  242.   with AnOp^ do
  243.     dest^ := trunc(arg1^) mod trunc(arg2^);
  244. end;
  245.  
  246. procedure _IntDiv(AnOp: POperation); far;
  247. begin
  248.   with AnOp^ do
  249.     dest^ := trunc(arg1^) div trunc(arg2^);
  250. end;
  251.  
  252. procedure _Negate(AnOp: POperation); far;
  253. begin
  254.   with AnOp^ do
  255.     dest^ := -arg1^;
  256. end;
  257.  
  258. procedure _IntPower(AnOp: POperation); far;
  259. {$IFNDEF UseMath}
  260. var
  261.   n, i: longint;
  262. {$ENDIF}
  263. begin
  264. {$IFNDEF UseMath}
  265.   with AnOp^ do
  266.   begin
  267.     n := trunc(abs(arg2^))-1;
  268.  
  269.     case n of
  270.       -1: dest^ := 1;
  271.        0: dest^ := arg1^;
  272.     else
  273.       dest^ := arg1^;
  274.       for i := 1 to n do
  275.         dest^ := dest^ * arg1^;
  276.     end;
  277.  
  278.     if arg2^ < 0 then
  279.       dest^ := 1 / dest^;
  280.  
  281.   end;
  282. {$ELSE}
  283.   with AnOp^ do
  284.     dest^ := IntPower(arg1^, trunc(arg2^));
  285. {$ENDIF}
  286. end;
  287.  
  288. procedure _square(AnOp: POperation); far;
  289. begin
  290.   with AnOp^ do
  291.     dest^ := sqr(arg1^);
  292. end;
  293.  
  294. procedure _third(AnOp: POperation); far;
  295. begin
  296.   with AnOp^ do
  297.     dest^ := arg1^ * arg1^ * arg1^;
  298. end;
  299.  
  300. procedure _forth(AnOp: POperation); far;
  301. begin
  302.   with AnOp^ do
  303.     dest^ := sqr(sqr(arg1^));
  304. end;
  305.  
  306. procedure _power(AnOp: POperation); far;
  307. begin
  308.   with AnOp^ do
  309.   begin
  310. {$IFNDEF UseMath}
  311.     if arg1^ = 0 then
  312.       dest^ := 0
  313.     else
  314.       dest^ := exp(arg2^*ln(arg1^));
  315. {$ELSE}
  316.     dest^ := Power(arg1^, arg2^);
  317. {$ENDIF}
  318.   end;
  319. end;
  320.  
  321. {****************************************************************}
  322. {                                                                }
  323. {   These are OPTIONAL procedures - you may remove them, though  }
  324. {   it is preferable to not register them for use                }
  325. {                                                                }
  326. {****************************************************************}
  327.  
  328. procedure _sin(AnOp: POperation); far;
  329. begin
  330.   with AnOp^ do
  331.     dest^ := sin(arg1^);
  332. end;
  333.  
  334. procedure _cos(AnOp: POperation); far;
  335. begin
  336.   with AnOp^ do
  337.     dest^ := cos(arg1^);
  338. end;
  339.  
  340. procedure _arctan(AnOp: POperation); far;
  341. begin
  342.   with AnOp^ do
  343.     dest^ := arctan(arg1^);
  344. end;
  345.  
  346. procedure _arg(AnOp: POperation); far;
  347. begin
  348.   with AnOp^ do
  349.     if arg1^ < 0 then
  350.       dest^ := arctan(arg2^/arg1^)+Pi
  351.     else
  352.       if arg1^>0 then
  353.         dest^ := arctan(arg2^/arg1^)
  354.       else
  355.         if arg2^ > 0 then
  356.           dest^ := 0.5 * Pi
  357.         else
  358.           dest^ := -0.5 * Pi;
  359. end;
  360.  
  361. procedure _sinh(AnOp: POperation); far;
  362. begin
  363.   with AnOp^ do
  364.     dest^ := (exp(arg1^)-exp(-arg1^))*0.5;
  365. end;
  366.  
  367. procedure _cosh(AnOp: POperation); far;
  368. begin
  369.   with AnOp^ do
  370.     dest^ := (exp(arg1^)+exp(-arg1^))*0.5;
  371. end;
  372.  
  373. procedure _cotan(AnOp: POperation); far;
  374. begin
  375.   with AnOp^ do
  376.   {$IFNDEF UseMath}
  377.     dest^ := cos(arg1^) / sin(arg1^);
  378.   {$ELSE}
  379.     dest^ := cotan(arg1^);
  380.   {$ENDIF}
  381. end;
  382.  
  383. procedure _tan(AnOp: POperation); far;
  384. begin
  385.   with AnOp^ do
  386.   {$IFNDEF UseMath}
  387.     dest^ := sin(arg1^) / cos(arg1^);
  388.   {$ELSE}
  389.     dest^ := tan(arg1^);
  390.   {$ENDIF}
  391. end;
  392.  
  393. procedure _exp(AnOp: POperation); far;
  394. begin
  395.   with AnOp^ do
  396.     dest^ := exp(arg1^);
  397. end;
  398.  
  399. procedure _ln(AnOp: POperation); far;
  400. begin
  401.   with AnOp^ do
  402.     dest^ := ln(arg1^);
  403. end;
  404.  
  405. procedure _log10(AnOp: POperation); far;
  406. const
  407.   _1_ln10 =  0.4342944819033;
  408. begin
  409.   with AnOp^ do
  410. {$IFDEF UseMath}
  411.     dest^ := log10(arg1^);
  412. {$ELSE}
  413.     dest^ := ln(arg1^) * _1_ln10;
  414. {$ENDIF}
  415. end;
  416.  
  417. procedure _log2(AnOp: POperation); far;
  418. const
  419.   _1_ln2 = 1.4426950409;
  420. begin
  421.   with AnOp^ do
  422. {$IFDEF UseMath}
  423.     dest^ := log2(arg1^);
  424. {$ELSE}
  425.     dest^ := ln(arg1^) * _1_ln2;
  426. {$ENDIF}
  427. end;
  428.  
  429. procedure _logN(AnOp: POperation); far;
  430. begin
  431.   with AnOp^ do
  432. {$IFDEF UseMath}
  433.     dest^ := logN(arg1^, arg2^);
  434. {$ELSE}
  435.     dest^ := ln(arg1^) / ln(arg2^);
  436. {$ENDIF}
  437. end;
  438.  
  439. procedure _sqrt(AnOp: POperation); far;
  440. begin
  441.   with AnOp^ do
  442.     dest^ := sqrt(arg1^);
  443. end;
  444.  
  445.  
  446. procedure _abs(AnOp: POperation); far;
  447. begin
  448.   with AnOp^ do
  449.     dest^ := abs(arg1^);
  450. end;
  451.  
  452. procedure _min(AnOp: POperation); far;
  453. begin
  454.   with AnOp^ do
  455.     if arg1^ < arg2^ then
  456.       dest^ := arg1^
  457.     else
  458.       dest^ := arg2^;
  459. end;
  460.  
  461. procedure _max(AnOp: POperation); far;
  462. begin
  463.   with AnOp^ do
  464.     if arg1^ < arg2^ then
  465.       dest^ := arg2^
  466.     else
  467.       dest^ := arg1^;
  468. end;
  469.  
  470. procedure _heaviside(AnOp: POperation); far;
  471. begin
  472.   with AnOp^ do
  473.     if arg1^ < 0 then
  474.       dest^ := 0
  475.     else
  476.       dest^ := 1;
  477. end;
  478.  
  479. procedure _sign(AnOp: POperation); far;
  480. begin
  481.   with AnOp^ do
  482.     if arg1^ < 0 then
  483.       dest^ := -1
  484.     else
  485.       if arg1^ > 0 then
  486.         dest^ := 1.0
  487.       else
  488.         dest^ := 0.0;
  489. end;
  490.  
  491. procedure _zero(AnOp: POperation); far;
  492. begin
  493.   with AnOp^ do
  494.     if arg1^ = 0.0 then
  495.       dest^ := 0.0
  496.     else
  497.       dest^ := 1.0;
  498. end;
  499.  
  500. procedure _trunc(AnOp: POperation); far;
  501. begin
  502.   with AnOp^ do
  503.     dest^ := int(arg1^)
  504. end;
  505.  
  506. procedure _ceil(AnOp: POperation); far;
  507. begin
  508.   with AnOp^ do
  509.     if frac(arg1^) > 0 then
  510.       dest^ := int(arg1^ + 1)
  511.     else
  512.       dest^ := int(arg1^);
  513. end;
  514.  
  515. procedure _floor(AnOp: POperation); far;
  516. begin
  517.   with AnOp^ do
  518.     if frac(arg1^) < 0 then
  519.       dest^ := int(arg1^ - 1)
  520.     else
  521.       dest^ := int(arg1^);
  522. end;
  523.  
  524. procedure _rnd(AnOp: POperation); far;
  525. begin
  526.   with AnOp^ do
  527.     dest^ := Random * int(arg1^);
  528. end;
  529.  
  530. procedure _random(AnOp: POperation); far;
  531. begin
  532.   with AnOp^ do
  533.     dest^ := Random;
  534. end;
  535.  
  536. procedure _radius(AnOp: POperation); far;
  537. begin
  538.   with AnOp^ do
  539.     dest^ := sqrt(sqr(arg1^)+sqr(arg2^));
  540. end;
  541.  
  542. procedure _phase(AnOp: POperation); far;
  543. var
  544.   a: ParserFloat;
  545. begin
  546.   with AnOp^ do
  547.   begin
  548.     a := arg1^ / (2/pi);
  549.     dest^ := (2*pi) * (a-round(a));
  550.   end;
  551. end;
  552.  
  553. {****************************************************************}
  554. {                                                                }
  555. {   TCustomParser                                                }
  556. {                                                                }
  557. {    A base class which does not publish the variable properties }
  558. {    and adds no functions by default                            }
  559. {                                                                }
  560. {****************************************************************}
  561.  
  562. function TCustomParser.ParseExpression: boolean;
  563. begin
  564.   try
  565.     P10Build.ParseFunction( FExpression, FVariables, FunctionOne, FunctionTwo,
  566.       FPascalNumberformat, FStartOperationList, FParserError);
  567.     Result := True;
  568.   except
  569.     on E: EMathParserError do
  570.     begin
  571.       FParserError := True;
  572.  
  573.       if Assigned(FOnParserError) then
  574.       begin
  575.         FOnParserError(Self, E);
  576.         Result := False;
  577.       end
  578.       else
  579.         raise;
  580.     end;
  581.   end;
  582. end;
  583.  
  584. procedure TCustomParser.GenerateDynamicCode(OperationCount: integer);
  585. {$IFDEF DYNAMIC_CODE}
  586. type
  587.   PCallOperation = ^TCallOperation;
  588.   TCallOperation = packed record
  589.     MOV_EAX    : Byte;
  590.     LastOpAddr : POperation;
  591.     CALL       : Byte;
  592.     OFFSET     : Longint;
  593.   end;
  594.   PReturnLastOp = ^TReturnLastOp;
  595.   TReturnLastOp = packed record
  596.     MOV_EAX    : Byte;
  597.     LastOpAddr : POperation;
  598.     RET        : Byte;
  599.   end;
  600. const
  601.   CallInstruction   = $E8;
  602.   RetInstruction    = $C3;
  603.   MovEAXInstruction = $B8;
  604. var
  605.   ThisCallOperation : PCallOperation;
  606.   ReturnLastOp      : PReturnLastOp;
  607.   Operation: POperation;
  608. begin
  609.   { Now generate some code dynamically on the heap to call the operations }
  610.   if OperationCount > 0 then
  611.   begin
  612.     { Allocate a memory block of the right size }
  613.     GetMem(DynamicCode, (OperationCount * SizeOf(TCallOperation)) + SizeOf(TReturnLastOp));
  614.  
  615.     { Loop through the operations and build code as we go }
  616.     ThisCallOperation := DynamicCode;
  617.     Operation := FStartOperationList;
  618.     while True do
  619.     begin
  620.       with ThisCallOperation^ do
  621.       begin
  622.         MOV_EAX    := MovEAXInstruction;
  623.         LastOpAddr := Operation;
  624.         CALL       := CallInstruction;
  625.         OFFSET     := PChar(@Operation^.MathProc) - (PChar(@ThisCallOperation^.CALL) + 5);
  626.       end;
  627.       Inc(ThisCallOperation);
  628.       if Operation^.NextOperation = nil then
  629.         Break;
  630.       Operation := Operation^.NextOperation;
  631.     end;
  632.     { Add code to return the last node }
  633.     ReturnLastOp := PReturnLastOp(ThisCallOperation);
  634.     with ReturnLastOp^ do
  635.     begin
  636.       MOV_EAX    := MovEAXInstruction;
  637.       LastOpAddr := Operation;
  638.       RET        := RetInstruction;
  639.     end;
  640.   end;
  641. end;
  642. {$ELSE }
  643. begin
  644. end;
  645. {$ENDIF DYNAMIC_CODE}
  646.  
  647. function TCustomParser.ConnectMandatoryOperations: integer;
  648. const
  649.   MandatoryOperationMap: array[TToken] of TMathProcedure
  650.     = (_nothing, _nothing, _negate, _add, _subtract, _multiply, _RealDivide,
  651.        _Modulo, _IntDiv, _IntPower, _Power, _square, _third, _forth, nil, nil);
  652. var
  653.   O: POperation;
  654. begin
  655.   Result := 0;
  656.   O := FStartOperationList;
  657.   while O <> nil do
  658.   begin
  659.     if O^.Token in [variab..fourth] then
  660.       O^.MathProc := MandatoryOperationMap[O^.Token];
  661.     O := O^.NextOperation;
  662.     Inc(Result);
  663.   end;
  664. end;
  665.  
  666. procedure TCustomParser.SetExpression(const AnExpression: string);
  667. var
  668.   OperationCount: integer;
  669. begin
  670.   FreeExpression;
  671.   FExpression := AnExpression;
  672.   if FExpression <> '' then
  673.     if ParseExpression then
  674.     begin
  675.       OperationCount := ConnectMandatoryOperations;
  676.       GenerateDynamicCode(OperationCount);
  677.     end;
  678. end;
  679.  
  680. constructor TCustomParser.Create(AOwner: TComponent);
  681. begin
  682.   inherited Create(AOwner);
  683.   FPascalNumberformat := True;
  684.  
  685.   FVariables := TStringList.Create;
  686.   FVariables.Sorted := True;
  687.   FVariables.Duplicates := dupIgnore;
  688.  
  689.   FFunctionOne := TStringList.Create;
  690.   FunctionOne.Sorted := True;
  691.   FunctionOne.Duplicates := dupError;
  692.  
  693.   FFunctionTwo := TStringList.Create;
  694.   FunctionTwo.Sorted := True;
  695.   FunctionTwo.Duplicates := dupError;
  696. end;
  697.  
  698. destructor TCustomParser.Destroy;
  699. begin
  700.   FreeExpression;
  701.  
  702.   ClearVariables;
  703.   FVariables.Free;
  704.  
  705.   FunctionOne.Free;
  706.   FunctionTwo.Free;
  707.  
  708.   inherited Destroy;
  709. end;
  710.  
  711. procedure TCustomParser.SetVar(const VarName: string; const Value: Extended);
  712. begin
  713.   SetVariable(VarName, Value);
  714. end;
  715.  
  716. function TCustomParser.SetVariable(VarName: string; const Value: Extended): PParserFloat;
  717. var
  718.   i: integer;
  719. begin
  720.   { TString.Find is not case-sensitive }
  721.   if FVariables.Find(VarName, i) then
  722.   begin
  723.     Result := PParserFloat(FVariables.Objects[i]);
  724.     Result^ := Value;
  725.   end
  726.   else
  727.   begin
  728.     { is the variable name a valid identifier? }
  729.     if not IsValidIdent(VarName) then
  730.       raise EBadName.Create(VarName);
  731.  
  732.     { Convert to uppercase }
  733.     VarName := UpperCase(VarName);
  734.  
  735.     { check whether the variable contains any of the operators (DIV and MOD)
  736.       this would confuse the parser... }
  737.     if pos('DIV', VarName) <> 0 then
  738.         raise EBadName.Create(VarName);
  739.  
  740.     if pos('MOD', VarName) <> 0 then
  741.         raise EBadName.Create(VarName);
  742.  
  743.     New(Result);
  744.     Result^ := Value;
  745.  
  746.     FVariables.AddObject(VarName, TObject(Result));
  747.   end;
  748. end;
  749.  
  750. function TCustomParser.GetVariable(const VarName: string): Extended;
  751. var
  752.   i: integer;
  753. begin
  754.   if FVariables.Find(VarName, i) then
  755.     Result := PParserFloat(FVariables.Objects[i])^
  756.   else
  757.     Result := 0.0;
  758. end;
  759.  
  760. procedure TCustomParser.AddFunctionOneParam(const AFunctionName: string; const Func: TMathProcedure);
  761. begin
  762.   if IsValidIdent(AFunctionName) then
  763.     FunctionOne.AddObject(UpperCase(AFunctionName), TObject(@Func))
  764.   else
  765.     raise EBadName.Create(AFunctionName);
  766. end;
  767.  
  768. procedure TCustomParser.AddFunctionTwoParam(const AFunctionName: string; const Func: TMathProcedure);
  769. begin
  770.   if IsValidIdent(AFunctionName) then
  771.     FunctionTwo.AddObject(UpperCase(AFunctionName), TObject(@Func))
  772.   else
  773.     raise EBadName.Create(AFunctionName);
  774. end;
  775.  
  776. procedure TCustomParser.DisposeVariableFloatAt(Index: integer);
  777. var
  778.   APPFloat: PParserFloat;
  779. begin
  780.   APPFloat := PParserFloat(FVariables.Objects[Index]);
  781.   { Dispose only user-defined variables }
  782.   if not PointerToSelfInstance(Self, APPFloat) then
  783.     Dispose(APPFloat);
  784. end;
  785.  
  786. procedure TCustomParser.ClearVariables;
  787. var
  788.   i: integer;
  789. begin
  790.   for i := 0 to FVariables.Count-1 do
  791.     DisposeVariableFloatAt(i);
  792.   FVariables.Clear;
  793.   SetExpression(''); { invalidate expression }
  794. end;
  795.  
  796. procedure TCustomParser.ClearVariable(const AVarName: string);
  797. var
  798.   Index: integer;
  799. begin
  800.   if FVariables.Find(AVarName, Index) then
  801.   begin
  802.     DisposeVariableFloatAt(Index);
  803.     FVariables.Delete(Index);
  804.   end;
  805.   SetExpression(''); { invalidate expression }
  806. end;
  807.  
  808. function TCustomParser.VariableExists(const AVarName: string): boolean;
  809. var
  810.   Index: integer;
  811. begin
  812.   Result := FVariables.Find(AVarName, Index);
  813. end;
  814.  
  815. procedure TCustomParser.ClearFunctions;
  816. begin
  817.   FunctionOne.Clear;
  818.   FunctionTwo.Clear;
  819.   SetExpression(''); { invalidate expression }
  820. end;
  821.  
  822. procedure TCustomParser.ClearFunction(const AFunctionName: string);
  823. var
  824.   Index: integer;
  825. begin
  826.   if FunctionOne.Find(AFunctionName, Index) then
  827.   begin
  828.     FunctionOne.Delete(Index);
  829.     SetExpression(''); { invalidate expression }
  830.     Exit;
  831.   end;
  832.  
  833.   if FunctionTwo.Find(AFunctionName, Index) then
  834.   begin
  835.     FunctionTwo.Delete(Index);
  836.     SetExpression(''); { invalidate expression }
  837.   end;
  838. end;
  839.  
  840. procedure TCustomParser.FreeExpression;
  841. var
  842.   LastOP,
  843.   NextOP: POperation;
  844. begin
  845.   LastOP := FStartOperationList;
  846.  
  847.   while LastOP <> nil do
  848.   begin
  849.     NextOP := LastOP^.NextOperation;
  850.  
  851.     while NextOP <> nil do
  852.       with NextOP^ do
  853.       begin
  854.         if (Arg1 = lastop^.Arg1) or (Arg1 = lastop^.Arg2) or (Arg1 = lastop^.Dest) then
  855.           Arg1 := nil;
  856.  
  857.         if (Arg2 = lastop^.Arg1) or (Arg2 = lastop^.Arg2) or (Arg2 = lastop^.Dest) then
  858.           Arg2 := nil;
  859.  
  860.         if (Dest = lastop^.Arg1) or (Dest = lastop^.Arg2) or (Dest = lastop^.Dest) then
  861.           Dest := nil;
  862.  
  863.         NextOP := NextOperation;
  864.       end;
  865.  
  866.     with LastOP^ do
  867.     begin
  868.       if FVariables.IndexOfObject( TObject(Arg1)) >= 0 then Arg1 := nil;
  869.       if FVariables.IndexOfObject( TObject(Arg2)) >= 0 then Arg2 := nil;
  870.       if FVariables.IndexOfObject( TObject(Dest)) >= 0 then Dest := nil;
  871.  
  872.       if (Dest <> nil) and (Dest <> Arg2) and (Dest <> Arg1) then
  873.         dispose(Dest);
  874.  
  875.       if (Arg2 <> nil) and (Arg2 <> Arg1) then
  876.         dispose(Arg2);
  877.  
  878.       if (Arg1 <> nil) then
  879.         dispose(Arg1);
  880.     end;
  881.  
  882.     NextOP := LastOP^.NextOperation;
  883.     dispose(LastOP);
  884.     LastOP := NextOP;
  885.   end;
  886.  
  887.   FStartOperationList := nil;
  888.  
  889. {$IFDEF DYNAMIC_CODE}
  890.   if Assigned(DynamicCode) then
  891.   begin
  892.     FreeMem(DynamicCode);
  893.     DynamicCode := nil;
  894.   end;
  895. {$ENDIF DYNAMIC_CODE}
  896. end;
  897.  
  898. function TCustomParser.GetValue: Extended;
  899. {$IFDEF DYNAMIC_CODE}
  900. type
  901.   TCallOperationFunc = function: POperation;
  902. begin
  903.   if Assigned(DynamicCode) then
  904.     Result := TCallOperationFunc(DynamicCode)^.Dest^
  905.   else
  906.     Result := 0;
  907. end;
  908. {$ELSE}
  909. var
  910.   LastOP: POperation;
  911. begin
  912.   if FStartOperationList <> nil then
  913.   begin
  914.     LastOP := FStartOperationList;
  915.     while LastOP^.NextOperation <> nil do
  916.     begin
  917.       with LastOP^ do
  918.       begin
  919.         MathProc(LastOP);
  920.         LastOP := NextOperation;
  921.       end;
  922.     end;
  923.     LastOP^.MathProc(LastOP);
  924.     Result := LastOP^.Dest^;
  925.   end
  926.   else
  927.     Result := 0;
  928. end;
  929. {$ENDIF DYNAMIC_CODE}
  930.  
  931. procedure TCustomParser.SetValue(const Value: Extended);
  932. begin
  933.   { Dummy set routine - to fool the Object Inspector }
  934. end;
  935.  
  936. procedure TCustomParser.DefineBasicFunctions;
  937. begin
  938.   with FunctionOne do
  939.   begin
  940.     Capacity := 32;
  941.     AddObject('TAN'   , TObject(@_tan));
  942.     AddObject('SIN'   , TObject(@_sin));
  943.     AddObject('COS'   , TObject(@_cos));
  944.     AddObject('SINH'  , TObject(@_sinh));
  945.     AddObject('COSH'  , TObject(@_cosh));
  946.     AddObject('ARCTAN', TObject(@_arctan));
  947.     AddObject('COTAN' , TObject(@_cotan));
  948.     AddObject('ARG'   , TObject(@_arg));
  949.     AddObject('EXP'   , TObject(@_exp));
  950.     AddObject('LN'    , TObject(@_ln));
  951.     AddObject('LOG10' , TObject(@_log10));
  952.     AddObject('LOG2'  , TObject(@_log2));
  953.     AddObject('SQR'   , TObject(@_square));
  954.     AddObject('SQRT'  , TObject(@_sqrt));
  955.     AddObject('ABS'   , TObject(@_abs));
  956.     AddObject('TRUNC' , TObject(@_trunc));
  957.     AddObject('INT'   , TObject(@_trunc)); { NOTE: INT = TRUNC ! }
  958.     AddObject('CEIL'  , TObject(@_ceil));
  959.     AddObject('FLOOR' , TObject(@_floor));
  960.     AddObject('HEAV'  , TObject(@_heaviside));
  961.     AddObject('SIGN'  , TObject(@_sign));
  962.     AddObject('ZERO'  , TObject(@_zero));
  963.     AddObject('PH'    , TObject(@_phase));
  964.     AddObject('RND'   , TObject(@_rnd));
  965.     AddObject('RANDOM', TObject(@_random));
  966.   end;
  967.   with FunctionTwo do
  968.   begin
  969.     Capacity := 8;
  970.     AddObject('MAX'     , TObject(@_max));
  971.     AddObject('MIN'     , TObject(@_min));
  972.     AddObject('POWER'   , TObject(@_Power));
  973.     AddObject('INTPOWER', TObject(@_IntPower));
  974.     AddObject('LOGN'    , TObject(@_logN));
  975.   end;
  976. end;
  977.  
  978. {****************************************************************}
  979. {                                                                }
  980. {   TCustomParser                                                }
  981. {                                                                }
  982. {****************************************************************}
  983. constructor TParser.Create(AOwner: TComponent);
  984. begin
  985.   inherited Create(AOwner);
  986.   DefineBasicFunctions;
  987.   with FVariables do
  988.   begin
  989.     Capacity := 16;
  990.     AddObject( 'A', TObject(@FA));
  991.     AddObject( 'B', TObject(@FB));
  992.     AddObject( 'C', TObject(@FC));
  993.     AddObject( 'D', TObject(@FD));
  994.     AddObject( 'E', TObject(@FE));
  995.     AddObject( 'X', TObject(@FX));
  996.     AddObject( 'Y', TObject(@FY));
  997.     AddObject( 'T', TObject(@FT));
  998.   end;
  999. end;
  1000.  
  1001. end.
  1002.